from java.io import BufferedReader, IOException, InputStreamReader, OutputStreamWriter, PrintWriter
from java.lang import Thread, Runnable
from java.net import Socket
import java.util.UUID as UUID

## A socket handler, which receives input from a socket and posts it to a
## callback object.  Exposes connect(), disconnect(), and print(line)
## methods for interacting with a socket.
class SchmuckSocket(Runnable):
	def __init__(self, socket_id, host, port, callback):
		self.socket_id = socket_id
		self.host = host
		self.port = port
		self.callback = callback
		self.active = False
		self.connect()

	## Connect to the specified host and port.  Does not connect if the socket is active.
	def connect(self):
		if(self.active == False):
			self.socket = Socket(self.host, self.port)
			self.stdin = BufferedReader(InputStreamReader(self.socket.getInputStream()))
			self.stdout = PrintWriter(OutputStreamWriter(self.socket.getOutputStream()))

			self.active = True
			self.thread = Thread(self)
			self.thread.start()

	## Disconnect the socket.  Does nothing if the socket isn't active.
	def disconnect(self):
		if(self.active == True):
			self.active = False
			try:
				self.thread.join()
			except:
				pass

	## Prints text to a socket, closing the socket if any error is received
	def send(self, line):
		try:
			self.stdout.println(line)
			self.stdout.flush()
		except:
			self.active = False

	def run(self):
		# todo: this does not gracefully handle partial lines, e.g. prompts
		while(self.active):
			s = self.stdin.readLine()
			try:
				self.callback.receive_line(self.socket_id, s)
			except:
				self.active = False
			if(s is None):
				self.active = False
		try:
			self.callback.socket_closed(self.socket_id)
		except:
			pass

class SchmuckSocketManager(object):
	def __init__(self):
		self.sockets = {}

	# Open a socket to a given host/port combination and return the UUID of the socket	
	def connect(self, host, port, socket_callback):
		try:
			socket_id = UUID.randomUUID().toString()
			self.sockets[socket_id] = SchmuckSocket(socket_id, host, port, socket_callback)
			return socket_id
		except Exception, inst:
			raise

	def disconnect(self, socket_id):
		if(socket_id in self.sockets):
			self.sockets[socket_id].disconnect

	def remove(self, socket_id):
		self.disconnect(socket_id)
		del self.sockets[socket_id]

	def send(self, socket_id, line):
		if(socket_id in self.sockets):
			self.sockets[socket_id].send(line)
			
socket_manager = SchmuckSocketManager()